/* @flow */

import type Watcher from './watcher';
import { remove } from '../util/index';
import config from '../config';

let uid = 0;

/**
 * A dep is an observable that can have multiple
 * directives subscribing to it.
 *（dep是依赖对象、记录watcher对象、depend（）watcher记录对应的dep、发布通知）
 * dep是一个可观察对象，可以有多个指令订阅它。
 */
export default class Dep {
    // 静态属性，watcher 对象
    static target: ?Watcher;
    // dep 实例 Id
    id: number;
    // dep 实例对应的 watcher 对象/订阅者数组
    subs: Array<Watcher>;

    constructor() {
        this.id = uid++;
        this.subs = [];
    }
    // 添加新的订阅者 watcher 对象
    addSub(sub: Watcher) {
        this.subs.push(sub);
    }
    // 移除订阅者
    removeSub(sub: Watcher) {
        remove(this.subs, sub);
    }
    // 将观察对象和 watcher 建立依赖
    // Dep.target里就是watcher
    depend() {
        if (Dep.target) {
            Dep.target.addDep(this);
        }
    }
    // 发布通知
    notify() {
        // stabilize the subscriber list first
        // subs存储的watcher对象
        // 后续会向 this.subs 中增加新的watcher，但新增的 watcher 目前暂时不用处理
        const subs = this.subs.slice();
        if (process.env.NODE_ENV !== 'production' && !config.async) {
            // subs aren't sorted in scheduler if not running async
            // we need to sort them now to make sure they fire in correct
            // order
            // 排序，按照watcher的创建排序
            subs.sort((a, b) => a.id - b.id);
        }
        // 调用每个订阅者的update方法实现更新
        for (let i = 0, l = subs.length; i < l; i++) {
            subs[i].update();
        }
    }
}

// Dep.target 用来存放目前正在执行的目标watcher对象
// 全局唯一，同一时间只能有一个watcher被使用
// The current target watcher being evaluated.
// This is globally unique because only one watcher
// can be evaluated at a time.
Dep.target = null;
const targetStack = [];

// 入栈并将当前 watcher 赋值给 Dep.target
// 父子组件嵌套的时候先把父组件对应的 watcher 入栈，
// 再去处理子组件的 watcher，子组件的处理完毕后，再把父组件对应的 watcher 出栈，继续操作
export function pushTarget(target: ?Watcher) {
    targetStack.push(target);
    Dep.target = target;
}

export function popTarget() {
    // 出栈操作
    targetStack.pop();
    Dep.target = targetStack[targetStack.length - 1];
}

// vue2以后
// 每个组件都有mountComponent函数，在mountComponent创建了watcher对象，每个组件对应一个watcher
// A组件渲染子组件B，那么组件B先渲染，A组件的watcher会放到栈中，先渲染组件B，等子组件B渲染完后，子组件B的watcher出栈，再渲染父组件A
